package com.kcl.dfss;

import java.sql.Date;
import java.sql.Time;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

import android.app.Service;
import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

import com.kcl.dfss.http.CameraConnection;
import com.kcl.dfss.http.CameraConnection.ResponseHandler;
import com.kcl.dfss.utils.ApplicationData;
import com.kcl.dfss.utils.UsageRecord;

public class LocationService extends Service {
	public static final String TAG = "LocationService";
	private LocationManager mLocationManager = null;
	private LocationProvider mProvider = null;
	private LocationListener mListener = null;
	private Location mLastLocation = null;
	private Criteria mCriteria = null;
	private static Location mLatestLocation = null;
	private static float mDistance = 0.0f;
	private BlockingDeque<Location> mLocationQueue = null;
	private boolean startWork = false;
	private boolean startCheck = false;
	private static LocationService mInstance = null;
	private Thread mWorkerThread = null;

	private Thread mCameraCheckThread = null;
	private static boolean CameraConnected = false;
	private final int CHEKC_INTERVAL = 3000;

	private static List<CameraConnectionCallback> mCallbacks = new ArrayList<CameraConnectionCallback>();

	private static Time mStartTime = null;
	private Time mEndTime;
	
	private static boolean DEBUG = false;

	private CameraConnection mConnection = new CameraConnection(
			new ResponseHandler() {

				@Override
				public void handleResponse(int request_code, String response) {
					switch (request_code) {
					case CameraConnection.CODE_FIRMWARE_VERSION:
						// Log.d(TAG, "LocationService response is:" +
						// response);

						if (!response.equals("error")) { // 此处加！
							if (!CameraConnected) {
								Log.d(TAG, "LS Camera Connected");
								startCaculateDistance();
								CameraConnected = true;
								if (!mCallbacks.isEmpty()) {
									for (CameraConnectionCallback callback : mCallbacks) {
										callback.onCameraConnected();
									}
								}
							}
						} else {
							if (CameraConnected) {
								stopCaculateDistance();
								CameraConnected = false;
								if (!mCallbacks.isEmpty()) {
									for (CameraConnectionCallback callback : mCallbacks) {
										callback.onCameraDisconnected();
									}
								}
							}
						}
						break;
					}
				}
			}, this);

	public class ConnectionCheckThread extends Thread {
		public void run() {
			while (startCheck) {
				if (DEBUG) {
					CameraConnected = true;
					try {
						Thread.sleep(CHEKC_INTERVAL); // 每隔三秒执行一次获取camera版本号请求判断是否设备是否在线
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					continue;
				}
				if (mConnection.isCameraAvailable()) {
					mConnection.getFirmwareVersion();
				} else {
					if (CameraConnected) {
						stopCaculateDistance();
						CameraConnected = false;
						if (!mCallbacks.isEmpty()) {
							for (CameraConnectionCallback callback : mCallbacks) {
								callback.onCameraDisconnected();
							}
						}
					}
				}
				try {
					Thread.sleep(CHEKC_INTERVAL); // 每隔三秒执行一次获取camera版本号请求判断是否设备是否在线
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static boolean getCamemraConnected() {
		return DEBUG ? true : CameraConnected;
	}

	public class WorkThread extends Thread {
		public void run() {
			float[] results = new float[1];
			while (startWork) {
				Location location;
				try {
					location = mLocationQueue.take();
					Log.d(TAG,
							"take location " + "Long: "
									+ String.valueOf(location.getLongitude())
									+ " Latit:"
									+ String.valueOf(location.getLatitude())
									+ " Speed: "
									+ String.valueOf(location.getSpeed()));
					if (mLastLocation != null) {
						// Log.d(TAG,
						// "LastLocation "
						// + "Long: "
						// + String.valueOf(mLastLocation
						// .getLongitude())
						// + " Latit:"
						// + String.valueOf(mLastLocation
						// .getLatitude())
						// + " Speed: "
						// + String.valueOf(mLastLocation
						// .getSpeed()));
						Location.distanceBetween(mLastLocation.getLatitude(),
								mLastLocation.getLongitude(),
								location.getLatitude(),
								location.getLongitude(), results);

//						Log.d(TAG, "result is " + results[0]);
						float accuracy1 = mLastLocation.getAccuracy();
						float accuracy2 = location.getAccuracy();
						if (results[0] < accuracy1 / 2.0f) {
							mDistance += results[0];
						}
						updateDistanceToLog(mDistance);
					}
					mLastLocation = location;
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}
	}

	public static float getDistance() {
		return mDistance;
	}

	public static Location getLatestLocation() {
		return mLatestLocation;
	}

	public static int getRunningTime() {
		if (mStartTime == null) {
			return 0;
		} else {
			return (int) ((System.currentTimeMillis() - mStartTime.getTime()) / 1000);
		}
	}

	public int secondsBetweenTime(Time startTime, Time endTime) {
		long interval = endTime.getTime() - startTime.getTime();
		return (int) (interval / 1000.0f);
	}

	public void updateRecord() {
		if (mStartTime == null) {
			return;
		}
		Log.d(TAG, "mEndTime is:" + mEndTime.toString());
		Log.d(TAG, "mStartTime is:" + mStartTime.toString());
		UsageRecord usage = new UsageRecord(
				new Date(System.currentTimeMillis()).toString(),
				mStartTime.toString(), mEndTime.toString(), mDistance,
				mDistance / secondsBetweenTime(mStartTime, mEndTime) / 1000
						/ 3600);
		ApplicationData mData = MainActivity.getAppData();
		if (mData != null)
			mData.insertUsageRecord(usage);
	}

	public void stopCaculateDistance() {
		Log.d(TAG, "stopCaculateDistance");
		mEndTime = new Time(System.currentTimeMillis());
		startWork = false;
		if (mWorkerThread != null) {
			mWorkerThread.interrupt();
		}
		mWorkerThread = null;
		if (mListener != null) {
			mLocationManager.removeUpdates(mListener);
		}
		mListener = null;
		updateRecord();
		mDistance = 0.0f;
	}

	public void startCaculateDistance() {
		Log.d(TAG, "startCaculateDistance");
		mStartTime = new Time(System.currentTimeMillis());
		startWork = true;
		mListener = new LocationListener() {

			@Override
			public void onLocationChanged(Location location) {
				try {
					updateLocationToLog(location);
					// if (location.getAccuracy() < 50) {
					mLatestLocation = location;
					mLocationQueue.put(location);
					// }
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			@Override
			public void onStatusChanged(String provider, int status,
					Bundle extras) {
				Log.d(TAG,"onStatusChanged  "+ provider  );
			}

			@Override
			public void onProviderEnabled(String provider) {

			}

			@Override
			public void onProviderDisabled(String provider) {
				if (startWork)
					stopCaculateDistance();
			}
		};

		// mLocat
		// mLocationManager.requestLocationUpdates(0, 0, new Criteria(),
		// mListener, getMainLooper());
		Criteria criteria = new Criteria();
		// 设置定位精确度 Criteria.ACCURACY_COARSE比较粗略，Criteria.ACCURACY_FINE则比较精细
		criteria.setAccuracy(Criteria.ACCURACY_FINE);
		// 设置是否要求速度
		criteria.setSpeedRequired(true);
		// 设置是否允许运营商收费
		criteria.setCostAllowed(false);
		// 设置是否需要方位信息
		criteria.setBearingRequired(false);
		// 设置是否需要海拔信息
		criteria.setAltitudeRequired(false);
		// 设置对电源的需求
		criteria.setPowerRequirement(Criteria.POWER_LOW);

		mLocationManager.requestLocationUpdates(
				mLocationManager.getBestProvider(criteria, false), 100, 0,
				mListener);

		mWorkerThread = new WorkThread();
		mWorkerThread.start();
	}

	// ==============================================================================

	public void onCreate() {
		super.onCreate();
		Log.d(TAG, "onCreate");
		mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
		mCriteria = new Criteria();
		mCriteria.setAccuracy(Criteria.ACCURACY_FINE);
		mLocationQueue = new LinkedBlockingDeque<Location>();
		mInstance = this;

		startCheck = true;
		mCameraCheckThread = new ConnectionCheckThread();
		mCameraCheckThread.start();

		if (DEBUG)
			startCaculateDistance();
	}

	public static LocationService getInstance() {
		return mInstance;
	}

	public void onDestroy() {
		super.onDestroy();
		mInstance = null;
		stopCaculateDistance();
		startCheck = false;
		mCameraCheckThread = null;
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	public void updateLocationToLog(Location location) {
		TestActivity.addLocationDataToView(location.getLongitude(),
				location.getLatitude(), location.getSpeed(),
				location.getAccuracy());
	}

	public void updateDistanceToLog(float distance) {
		TestActivity.addDistanceToView(distance);
	}

	public static void addCameraCallback(CameraConnectionCallback callback) {
		if (callback != null) {
			mCallbacks.add(callback);
		}
	}

	public static void removeCameraCallback(CameraConnectionCallback callback) {
		if (callback != null) {
			if (mCallbacks.contains(callback)) {
				mCallbacks.remove(callback);
			}
		}
	}

	public interface CameraConnectionCallback {
		public void onCameraConnected();

		public void onCameraDisconnected();
	}

}